package cs5300;

import java.net.DatagramPacket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import BasicDataType.CookieType;
import BasicDataType.IPP;
import RPC.RPCConstants;
import RPC.RPCclient;
import RPC.RPCserver;
import RPC.ResponseMessage;

public class SessionManager {
	static SessionManager sessionManager;
	static CacheTable cTable = CacheTable.getInstance();
	static SessionTable sTable = SessionTable.getInstance();
	static RPCserver rpcServer;
	static RPCclient rpcclient;
	indexDisplayWrapper displayWrapper;
	public static final String CookieName = "CS5300PROJECT1SESSION";
	public static final int COOKIEEXPIRETIME = 120;
	public static String REFRESH = "refresh";
	public static String UPDATE = "update";

	// static int SessionId = 0;

	SessionManager() {
		if (rpcServer == null) {
			rpcServer = new RPCserver(RPCConstants.SREVERPORT);
			Thread t = new Thread(rpcServer);
			t.start();
		}
		if (rpcclient == null) {
			rpcclient = new RPCclient();
		}
	}

	public static SessionManager getInstance() {
		if (sessionManager == null) {
			sessionManager = new SessionManager();
		}
		return sessionManager;
	}

	public void update(HttpServletRequest request, HttpServletResponse response) {
		indexDisplayWrapper.initial();
		indexDisplayWrapper.UpdateMbrSet();
		indexDisplayWrapper.UpdateServerEXE(RPCserver.serverAddress);
		Cookie[] cookies = request.getCookies();
		Cookie firstCookie = null;
		String message = request.getParameter("ReplaceText");
		if (message == null) {
			message = "Hello User~!";
		} else {
			if (message.isEmpty()) {
				message = "Hello User~!";
			}
		}
		String ope = (String) request.getAttribute("OPE");
		if (ope == null) {
			ope = REFRESH;
		}
		boolean hasCookie = true;
		if (cookies == null) {
			indexDisplayWrapper.evict = "no Cookie Set the has Cookie to false--->";
			hasCookie = false;
		} else {
			indexDisplayWrapper.evict = "has some Cookie\n--->";
			for (int i = 0; i < cookies.length; i++) {
				if (cookies[i].getName().equals(SessionManager.CookieName)) {
					firstCookie = cookies[i];
					break;
				}
			}
			// find the cookie
			if (firstCookie != null) {
				indexDisplayWrapper.evict += "Cookie name Right\n--->";
				CookieType cookieT = new CookieType(firstCookie.getValue());
				// rpcclient.addMemberList(cookieT.ippPrimary);
				// rpcclient.addMemberList(cookieT.backup);

				// search in cTable
				if (cTable.getState(cookieT.sessionID) != null) {
					if (validCookie(firstCookie)) {
						indexDisplayWrapper.evict += "cookie found in cTable";
						hasCookie = true;
						if (ope == UPDATE) {// update in local cTable
							CookieType newcookieT = new CookieType(firstCookie.getValue());
							sessionState newSs = sTable.getState(newcookieT.sessionID);
							newSs.cookie.change_count++;
							newSs.discard_time = sessionState.getdisCardTime();
							newSs.message = message;
							// TODO: when read, don't update cach; when
							// write, update cache
							rpcclient.sessionWrite(newSs);// update new
															// session in
															// backup server
							cTable.updateState(newSs);// update new session
														// in cTable
							indexDisplayWrapper.message = message;
							indexDisplayWrapper.UpdateDiscardTime(newSs.discard_time);
							Cookie cookie = new Cookie(CookieName, newSs.cookie.getString());
						} else {// refresh in local cTable
							CookieType newcookieT = new CookieType(firstCookie.getValue());
							sessionState newSs = cTable.getState(newcookieT.sessionID);
							indexDisplayWrapper.message = newSs.message;
							indexDisplayWrapper.UpdateDiscardTime(newSs.discard_time);
						}
					}
				} else {
					// Search in sTable
					sessionState localSession = sTable.getState(cookieT.sessionID);
					if (localSession == null) {
						indexDisplayWrapper.evict += "local not found \n--->";
						// ask other server
						DatagramPacket dgp = rpcclient.sessionRead(cookieT);
						if (dgp == null) {
							indexDisplayWrapper.evict += "Server not found \n--->";
							hasCookie = false;
						} else {
							indexDisplayWrapper.evict += "Other Server found \n--->";
							IPP serverFound = new IPP();
							serverFound.IPaddress = dgp.getAddress();
							serverFound.Port = dgp.getPort();
							indexDisplayWrapper.UpdateServerFound(serverFound);
							ResponseMessage resM = new ResponseMessage(new String(dgp.getData()));
							sessionState recieveSS = sessionState.getSessionState(resM.result);
							if (ope == UPDATE) {
								rpcclient.sessionDelete(recieveSS.cookie);
								indexDisplayWrapper.evict += "Do Update--->";
								recieveSS.cookie.change_count++;
								recieveSS.discard_time = sessionState.getdisCardTime();
								recieveSS.message = message;
								recieveSS.cookie.backup = recieveSS.cookie.ippPrimary;
								recieveSS.cookie.ippPrimary = RPCserver.serverAddress;
								rpcclient.sessionWrite(recieveSS);
								sTable.insertSession(recieveSS);
								indexDisplayWrapper.UpdateDiscardTime(recieveSS.discard_time);
								indexDisplayWrapper.message = recieveSS.cookie.sessionID + ":" + message;
								Cookie newCookie = new Cookie(CookieName, recieveSS.cookie.getString());
								response.addCookie(newCookie);
							} else if (ope == REFRESH) {
								indexDisplayWrapper.evict += "Do Refresh--->";
								indexDisplayWrapper.UpdateDiscardTime(recieveSS.discard_time);
								indexDisplayWrapper.message = recieveSS.cookie.sessionID + ":" + recieveSS.message;
							}
						}
					} else {
						// local found
						indexDisplayWrapper.evict += "local found--->";
						indexDisplayWrapper.UpdateServerFound(rpcServer.serverAddress);
						if (ope == UPDATE) {
							indexDisplayWrapper.evict += "Do UPDATE---->";
							rpcclient.sessionDelete(localSession.cookie);
							localSession.cookie.change_count++;
							localSession.cookie.ippPrimary = RPCserver.serverAddress;
							localSession.cookie.backup = null;
							localSession.message = message;
							localSession.discard_time = sessionState.getdisCardTime();
							rpcclient.sessionWrite(localSession);
							sTable.updateState(localSession);
							cTable.insertSession(localSession);
							indexDisplayWrapper.UpdateDiscardTime(localSession.discard_time);
							indexDisplayWrapper.message = localSession.cookie.sessionID + ":" + localSession.message;
							Cookie newCookie = new Cookie(CookieName, localSession.cookie.getString());
							response.addCookie(newCookie);
						} else if (ope == REFRESH) {
							indexDisplayWrapper.evict += "Do Refresh--->";
							indexDisplayWrapper.UpdateDiscardTime(localSession.discard_time);
							indexDisplayWrapper.message = localSession.cookie.sessionID + ":" + localSession.message;
						}

					}
					// find locally
				}
			} else {
				indexDisplayWrapper.evict += "Cookie name Not Found\n--->";
				hasCookie = false;
			}
		}
		if (!hasCookie) {
			Cookie cookie = getNewCookie();
			indexDisplayWrapper.UpdateServerFound(RPCserver.serverAddress);
			response.addCookie(cookie);
		}

	}

	/**
	 * given the sessionID and return the formated String of the session expire
	 * time
	 * 
	 * @param sessionID
	 * @return formated String of the session expire time
	 */
	public String getSessionTimeString(String sessionID) {
		sessionState ss = sTable.getState(sessionID);
		Date time = new Date();
		time.setTime(ss.discard_time);
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd G 'at' hh:mm:ss z");
		return sdf.format(time);
	}

	/**
	 * generate a new cookie and add the cookie information into hashtable
	 * 
	 * @return
	 */
	public Cookie getNewCookie() {
		CookieType cookieT = new CookieType(RPCserver.serverAddress);
		indexDisplayWrapper.UpdateServerEXE(RPCserver.serverAddress);
		cookieT.ippPrimary = RPCserver.serverAddress;
		cookieT.backup = RPCclient.getRandomServer();
		// insert the session at the local server
		sessionState sState = new sessionState();
		indexDisplayWrapper.UpdateDiscardTime(sState.discard_time);
		indexDisplayWrapper.message = cookieT.sessionID + sState.message + "new one" + Integer.toString(sTable.sessionTable.size());
		sState.cookie = cookieT;
		rpcclient.sessionWrite(sState);
		sTable.insertSession(sState);
		Cookie cookie = new Cookie(CookieName, cookieT.getString());
		return cookie;
	}

	/**
	 * given the sessionID, return the sessionState class according to the
	 * sessionID
	 * 
	 * @param sessionID
	 * @return
	 */
	public sessionState getSessionState(String sessionID) {
		sessionState ss = sTable.getState(sessionID);
		return ss;
	}

	/**
	 * Check if the Cookie is valid or not
	 * 
	 * @param cookie
	 * @return true is valid false is not valid
	 */
	public boolean validCookie(CookieType cookie) {
		sessionState ss = sTable.getState(cookie.sessionID);
		if (ss == null) {
			return true;
		}
		Calendar cal = Calendar.getInstance();
		return (cal.getTimeInMillis() + sessionState.SESSION_TIMEOUT_SECS < ss.discard_time);
	}

	public boolean validCookie(Cookie cookie) {
		sessionState ss = sTable.getState(cookie.getValue());
		if (ss == null) {
			return true;
		}
		Calendar cal = Calendar.getInstance();
		return (cal.getTimeInMillis() + sessionState.SESSION_TIMEOUT_SECS < ss.discard_time);
	}

	/**
	 * update a existing Cookie
	 * 
	 * @param cookie
	 * @param ChangeVersion
	 *            true than change the version, and vice versa
	 * @param messege
	 *            the messege for the seesion
	 */
	public void UpdateCookie(Cookie cookie, boolean ChangeVersion, String messege) {
		try {
			/*
			 * CookieType cookieT = new CookieType(cookie.getValue()); String
			 * seID = cookieT.sessionID; cookieT.ippPrimary = sessionState ss =
			 * new sessionState(); ss.message = messege;
			 * 
			 * if(ChangeVersion){ int newV = cookie.getVersion();
			 * cookie.setVersion(newV++); ss.message = messege; }
			 * sTable.updateState(seID, ss);
			 */
		} catch (Exception e) {
			System.out.println(e.getLocalizedMessage());
		}
	}
}
